package la.bbs558.websocket;

import cn.dev33.satoken.stp.StpUtil;
import la.bbs558.service.UserMessageService;
import la.bbs558.util.ApplicationUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName WebSocketServer
 * @Description: websocket 服务
 * @ServerEndpoint 通过这个 spring boot 就可以知道你暴露出去的 websockst 应用的路径，
 * 有点类似我们经常用的@RequestMapping。比如你的启动端口是8080，而这个注解的值是ws，那我们就可以通过 ws://127.0.0.1:8080/websocket 来连接你的应用
 * @OnOpen 当 websocket 建立连接成功后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnClose 当 websocket 建立的连接断开后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnMessage 当客户端发送消息到服务端时，会触发这个注解修改的方法，它有一个 String 入参表明客户端传入的值
 * @OnError 当 websocket 建立连接时出现异常会触发这个注解修饰的方法，注意它有一个 Session 参数
 */
@Component
@ServerEndpoint("/webSocket/{token}")
@Slf4j
public class WebSocketServer {
    /**
     * concurrent包的线程安全Set，用来存放每个用户对应的Session对象。
     */
    public static final Map<String, Session> TOKEN_TO_SESSION = new ConcurrentHashMap<>();
    public static final Map<String, String> SESSION_ID_TO_TOKEN = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session) throws IOException {
        if (token == null) {
            return;
        }
        Object loginId = StpUtil.getLoginIdByToken(token);
        if(loginId != null){
            String userId = loginId + "";
            TOKEN_TO_SESSION.put(token, session);
            SESSION_ID_TO_TOKEN.put(session.getId(),token);
            UserMessageService userMessageService = ApplicationUtils.getBean(UserMessageService.class);
            userMessageService.websocketUpdateMsgCount(Long.parseLong(userId));
            log.info("用户id:{}，已连接到websocket服务器",userId);
        }else {
            log.info("用户token:{}，未登录，无法连接到websocket服务器",token);
            session.close();
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("token") String token,Session session) {
        String userId = SESSION_ID_TO_TOKEN.get(session.getId());
        SESSION_ID_TO_TOKEN.remove(session.getId());
        log.info("userId:[{}]已离开websocket服务器",userId);
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String msg,Session session) throws IOException {
        String id = session.getId();
        String userId = SESSION_ID_TO_TOKEN.get(id);
        if("ping".equals(msg)){
            session.getBasicRemote().sendText("pong");
        }else {
            log.info("userId:{}.发送的信息为：{}",userId,msg);
        }
    }

    /**
     * 出现异常触发的方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
        String userId = SESSION_ID_TO_TOKEN.get(session.getId());
        log.error("userId:{}发生错误，错误信息：{}",userId,error.getMessage());
    }


}
